//这个文件是用来判定是模式的，根据我们在page-noah实现阶段的测试，我们发现我们将在数据集是否连续和历史数据是否可以形成模式这两个问题上进行监控
//为了保存一个足够大的历史，我们使用一个链表来存储历史的最近n个访问记录
//然后我们使用两个变量来分别存储暂时的page预测错误次数和noah未进行预测次数。而怎么在短时间内预测page是否正确还是很难的。
//我觉得为page单独开一个逻辑上的、只有6位的LRU缓存就好。因为使用大的read_cache来判断反应太慢了。慢了10000个块。

//加入模式的判别，在一个数据集的不同位置模式算法不一样。

//现期先处理一个情景，那就是page顺序访问的正确率较高，但是模式积累不足的情况，也就是说我们现在只观察page预测器，如果预测器的错误率低于10%
//（这个阈值可以设定），那就在noah无法预测时候启用保守的page预测（预读三位）。否则还是老老实实用page+noah强行来。在后期的实现中我们需要
//根据模式把对于参数的调整也体现出来。

#include "mode-judge.h"
#include <memory.h>


void init_mode_judge(mode_judge_t* mode_judger){
    //创造一个伪读缓存
    init_single_readcache(&(mode_judger->page_logic_readcache),MAX_MODE_JUDGE_PAGE_LOGIC_CACHE_SIZE);

    //头结点
    mode_judger->history_head = (read_cache_meta_t *)malloc(sizeof(read_cache_meta_t));
    memset(mode_judger->history_head, 0, sizeof(read_cache_meta_t));
    
    //将1000按照时序排列的历史记录初始化
    mode_judger->history_head->front = mode_judger->history_head;
    mode_judger->history_head->next = mode_judger->history_head;
    mode_judger->history_head->have_accessed = -1;
    mode_judger->history_head->block_num = -1;

    mode_judger->page_err_predict = 0;
    mode_judger->page_history_size = 0;

    printf("模式检测器初始化完毕\n");
}

//更新page预测器的正确率，并且返回响应的模式，1代表模式不清，但是page预测器正确率较高情况
//0代表其他情况
//将预测结果更新到模式判断器中
void update_and_judge_mode(mode_judge_t* mode_judger, long predictor_page){
    // printf("更新数据%ld\n", predictor_page);
    //mode_judge的更新
    //将数据加入到逻辑的缓存中，并且将从缓存中剔除的元素放到1000位的历史记录表中
    read_cache_meta_t* rubbish_meta = add_single_cache_meta(&(mode_judger->page_logic_readcache), MAX_MODE_JUDGE_PAGE_LOGIC_CACHE_SIZE, predictor_page);
    
    //如果没有淘汰出来的块，那就直接返回
    if(rubbish_meta != NULL){
        //将淘汰出来的块存储起来，好计算当前的错误率，位置插入在历史记录链表头部
        read_cache_meta_t* head = mode_judger->history_head;
        read_cache_meta_t* head_next = head->next;

        head->next = rubbish_meta;
        rubbish_meta->front = head;

        head_next->front = rubbish_meta;
        rubbish_meta->next = head_next;

        //修改总预测量和预测错误量
        (mode_judger->page_history_size)++;

        if(rubbish_meta->have_accessed == 0){
            mode_judger->page_err_predict = mode_judger->page_err_predict + 1;
        }

        //看看容量有没有超
        if(mode_judger->page_history_size > MAX_MODE_JUDGE_PAGE_HISTORY_SIZE){
            //如果超了就从尾部退出
            read_cache_meta_t* head_front = head->front;
            //将head_front删除
            head_front->next->front = head_front->front;
            head_front->front->next = head_front->next;

            //更新元数据
            (mode_judger->page_history_size)--;

            if(head_front->have_accessed == 0){
                mode_judger->page_err_predict = mode_judger->page_err_predict - 1;
            }
            
            free(head_front);
        }

        //开始计算错误率
        if(mode_judger->page_history_size == 0){
            // printf("没有mode_judger历史记录\n");
            mode_judger->page_err_rate = 0;
        }else{
            mode_judger->page_err_rate = mode_judger->page_err_predict * 100 / mode_judger->page_history_size;
        }
    }    
}

//用一个函数来记录用户的访问，从而修改逻辑page_cache中的have_access值
void update_when_access_page_cache(mode_judge_t* mode_judger, long now_access){
    // printf("更新数据\n");
    //更新一下已访问元素
    read_cache_meta_t* scan = mode_judger->page_logic_readcache.head->next;

    //访问
    while(scan != mode_judger->page_logic_readcache.head){
        //查看在不在cache中
        // printf("扫描到的数据%ld与要被修改的数据%ld\n", scan->block_num, now_access);
        if(scan->block_num == now_access){
            //修改have_access
            // printf("发现一个数据被修改\n");
            scan->have_accessed = 1;
            break;
        }
        scan = scan->next;
    }

    //这里就修改完了
}

//获取当前的模式，并且需要等待一会才有说服力
int get_mode_of_predictor(mode_judge_t* mode_judger){
    //返回类型，如果错误率低于10%，那么就大胆使用这种预测，当然这个可以作为一个可配置项
    if(mode_judger->page_err_rate < 20 && mode_judger->page_history_size > MAX_MODE_JUDGE_PAGE_HISTORY_SIZE - 1){
        return 1;
    }else{
        return 0;
    }
}

